Skip to content

feat(system): Load Uniscribe at runtime - #3241

Open
OmarAglan wants to merge 1 commit into
TheSuperHackers:mainfrom
OmarAglan:feature/usp10-loader
Open

feat(system): Load Uniscribe at runtime#3241
OmarAglan wants to merge 1 commit into
TheSuperHackers:mainfrom
OmarAglan:feature/usp10-loader

Conversation

@OmarAglan

@OmarAglan OmarAglan commented Aug 31, 2026

Copy link
Copy Markdown

Adds a small Windows-only runtime loader for the Uniscribe entry points required by the complex-text renderer.

The loader resolves the functions from the system usp10.dll at runtime and reports failure to callers when Uniscribe is unavailable. This allows #3231 to retain the existing per-character renderer as its fallback while removing the compile-time dependency on usp10.h and usp10.lib, which are missing from the VC6 SDK.

The loader also provides synchronized shutdown cleanup; #3231 calls it from WW3D::Shutdown().

Cross-platform text shaping remains outside this focused compatibility change.

The stacked result was validated with:

  • Visual Studio 2022 x86 Release builds of Generals and Zero Hour
  • The repository’s portable VC6 toolchain for Generals and Zero Hour
  • Verification that the resulting executables do not statically import usp10.dll
  • git diff --check

The change was developed with AI assistance, then manually reviewed against the nearby runtime-loader pattern and the official Windows SDK function declarations.

@OmarAglan
OmarAglan marked this pull request as ready for review August 31, 2026 20:18
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Load required Uniscribe APIs dynamically on Windows

✨ Enhancement 🕐 10-20 Minutes

Grey Divider

AI Description

• Loads five Uniscribe APIs dynamically from Windows’ system usp10.dll.
• Reports unavailable libraries or exports so renderers can retain existing fallbacks.
• Provides SDK-independent declarations compatible with the portable VC6 toolchain.
Diagram

graph TD
    Renderer["Text renderer"] --> Loader["Usp10 loader"] --> Attempt{"First attempt?"}
    Attempt -->|Yes| DLL["System usp10.dll"] --> Exports["Five exports"] --> Status["Cached status"]
    Attempt -->|No| Status
Loading
High-Level Assessment

The runtime-loader approach is appropriate for this compatibility-focused change: it avoids unavailable VC6 SDK headers and import libraries, prevents a static usp10.dll dependency, loads only from the Windows system directory, and exposes failure for the renderer’s existing fallback. Static linking and linker delay-loading were considered but would retain toolchain or import-table dependencies, while a general-purpose loader abstraction would add unnecessary scope for five tightly related APIs.

Files changed (3) +184 / -0

Enhancement (2) +182 / -0
Usp10Loader.cppResolve and wrap Uniscribe exports at runtime +111/-0

Resolve and wrap Uniscribe exports at runtime

• Implements thread-safe, one-time loading of 'usp10.dll' from the Windows system directory and resolves five required exports. Wrapper methods return 'E_FAIL' or 'nullptr' when the DLL or any required function is unavailable, allowing callers to fall back safely.

Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp

Usp10Loader.hDeclare SDK-independent Uniscribe wrappers +71/-0

Declare SDK-independent Uniscribe wrappers

• Defines the minimal opaque types, flags, function signatures, and loader state required by the complex-text renderer without including 'usp10.h'. The public static API mirrors the required Uniscribe calls while hiding dynamic-resolution details.

Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h

Other (1) +2 / -0
CMakeLists.txtBuild the Uniscribe loader on Windows +2/-0

Build the Uniscribe loader on Windows

• Adds the loader implementation and header to WWLib’s Windows-only source list, keeping non-Windows builds unaffected.

Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a Windows-only WWLib service that dynamically loads Uniscribe from the system directory, validates the required exports, and exposes wrappers that report failure when the service is unavailable.

  • Adds Usp10Loader declarations and runtime loading, cleanup, and forwarding logic.
  • Includes the loader in core_wwlib only for Windows builds.
  • Avoids static linkage to usp10.dll and compile-time dependence on usp10.h.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

Filename Overview
Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp Implements synchronized lazy loading, export validation, wrapper forwarding, and explicit resource cleanup for the system Uniscribe library.
Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h Defines the loader API and compatible Uniscribe types, flags, and function-pointer declarations without requiring the SDK header.
Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt Adds the loader sources to core_wwlib under the existing Windows-only source gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Caller[Text renderer caller] --> Wrapper[Usp10Loader wrapper]
    Wrapper --> Load{Loader initialized?}
    Load -->|No| SystemPath[Resolve Windows system directory]
    SystemPath --> DLL[Load usp10.dll]
    DLL --> Exports[Resolve and validate required exports]
    Exports -->|Failure| Fallback[Return failure to caller]
    Exports -->|Success| Invoke[Invoke Uniscribe function]
    Load -->|Yes| Invoke
    Invoke --> Result[Return Uniscribe result]
    Shutdown[Engine shutdown] --> Unload[Free DLL and clear exports]
Loading

Reviews (5): Last reviewed commit: "feat(system): Load Uniscribe at runtime" | Re-trigger Greptile

Comment thread Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h Outdated
Comment thread Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h
Comment thread Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp Outdated
@OmarAglan

Copy link
Copy Markdown
Author

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 68a4beb2f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meaning of USP10

USP10 stands for Uniscribe Script Processor version 1.0. This name reflects its primary function within the Windows operating system.

Functionality

  • Uniscribe: This component is responsible for handling the proper formatting of Unicode text, especially for complex scripts.
  • Script Processor: It processes text layout and rendering, ensuring that text appears correctly on the screen.

Versioning

  • The "10" in USP10 indicates that it is the first version of the Uniscribe Script Processor, which is integral to the Windows operating system's ability to manage various languages and scripts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If microsoft bumps the version number... 😆 Looks like they did not in 28 years.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, that's true, this is very old tech from win 98SE era, i suppose it now got replaced with direct write, this is adding support for Arabic or rtl language and bidi without rewriting the backend, see https://learn.microsoft.com/en-us/windows/win32/intl/uniscribe

also, it has serval minor versions, see https://scripts.sil.org/cms/scripts/page.php?id=uniscribeversions&site_id=nrsi

@OmarAglan
OmarAglan force-pushed the feature/usp10-loader branch from 9115581 to b49c7f7 Compare September 4, 2026 00:06
@OmarAglan
OmarAglan force-pushed the feature/usp10-loader branch from b49c7f7 to 61fa2cc Compare September 4, 2026 01:37

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61fa2ccf19

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


HRESULT Usp10Loader::ScriptIsComplex(const WCHAR *text, int text_length, DWORD flags)
{
return load() ? ScriptIsComplexPtr(text, text_length, flags) : E_FAIL;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the loader lock held while invoking exports

When unload() runs concurrently with any shaping call, load() releases CriticalSection before this expression reads and invokes the function pointer. The unloading thread can therefore call FreeLibrary() and null the pointers in that gap, causing the caller to invoke either a null pointer or code from an unloaded module. All export wrappers have this race; keep the lock held through each invocation or otherwise prevent unloading while calls are active.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants